/************************************************************************
* font.c
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
************************************************************************/

#include "common.h"
#include "path.h"
#define _WM_EXPOSE_ALL
#include "wm.h"
#include "graphics.h"

#include <string.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_OUTLINE_H
#include FT_TRIGONOMETRY_H

static uint32_t font_ttf_get_page_size(FT_Face face, uint32_t *mw, uint32_t *mh, int32_t *bl, uint32_t i)
{
	uint32_t w = 0;
	uint32_t h = 0;
	uint32_t ch;
	FT_Glyph glyph;
	FT_BitmapGlyph bitmap_glyph;

	*mh = 0;
	*mw = 0;
	*bl = 0;

	for (ch=0; ch<256; ch++) {
		if (FT_Load_Glyph(face,FT_Get_Char_Index(face,ch+(i*256)),FT_LOAD_DEFAULT))
			return 1;

		if (FT_Get_Glyph(face->glyph,&glyph))
			return 1;

		/* make a bitmap of the glyph */
		FT_Glyph_To_Bitmap(&glyph,ft_render_mode_normal,0,1);
		bitmap_glyph = (FT_BitmapGlyph)glyph;

		w = bitmap_glyph->bitmap.width;
		h = bitmap_glyph->bitmap.rows;

		if (h > *mh)
			*mh = h;
		if (w > *mw)
			*mw = w;
		if (bitmap_glyph->top > *bl)
			*bl = bitmap_glyph->top;
	}

	if (!(*mw) || !(*mh))
		return 0;

	*mw += 6;
	*mh += 6;

	w = (*mw)*16;
	h = (*mh)*16;

	if (w > h)
		return math_next_pow2(w);

	return math_next_pow2(h);
}

static int font_ttf_gen_fontpage(font_t *f, uint32_t i)
{
	GLint swizzleMask[4] = {GL_RED, GL_RED, GL_RED, GL_GREEN};
	FT_Face face;
	FT_Library library;
	FT_Glyph glyph;
	FT_BitmapGlyph bitmap_glyph;
	fontpage_t *p;
	fontchar_t *character;
	uint32_t s;
	uint32_t h = 32<<6;
	uint32_t ch;
	uint32_t gw;
	uint32_t gh;
	int32_t bl;
	uint32_t mh = 0;
	uint32_t mw = 0;
	uint32_t sx;
	uint32_t sy;
	uint32_t ax;
	uint32_t ay;
	int m = 0;
	int j;
	int k;
	int l;

	p = array_get_ptr(&f->pages,i);
	if (p)
		return 0;

	/* connect to freetype */
	if (FT_Init_FreeType(&library))
		return 1;

	if (FT_New_Face(library,f->file,0,&face)) {
		FT_Done_FreeType(library);
		return 1;
	}

	if (FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
		FT_Done_Face(face);
		FT_Done_FreeType(library);
		return 1;
	}

	/* something something characters something something */
	if (FT_Set_Char_Size(face,h,h,96,96)) {
		FT_Done_Face(face);
		FT_Done_FreeType(library);
		return 1;
	}

	s = font_ttf_get_page_size(face,&mw,&mh,&bl,i);

	if (!s) {
		FT_Done_Face(face);
		FT_Done_FreeType(library);
		return 1;
	}

	p = malloc(sizeof(fontpage_t));
	if (!p) {
		FT_Done_Face(face);
		FT_Done_FreeType(library);
		return 1;
	}

	gw = 2*s*s;

	p->pixels = malloc(gw);
	if (!p->pixels) {
		free(p);
		FT_Done_Face(face);
		FT_Done_FreeType(library);
		return 1;
	}

	array_init(&p->sizes,ARRAY_TYPE_PTR);

	memset(p->pixels,0,gw);

	sx = 0;
	sy = 0;

	for (ch=0; ch<256; ch++) {
		if (FT_Load_Glyph(face,FT_Get_Char_Index(face,ch+(i*256)),FT_LOAD_DEFAULT))
			return 1;

		if (FT_Get_Glyph(face->glyph,&glyph))
			return 1;

		/* make a bitmap of the glyph */
		FT_Glyph_To_Bitmap(&glyph,ft_render_mode_normal,0,1);
		bitmap_glyph = (FT_BitmapGlyph)glyph;

		/* TODO: a less shitty way to do this, should calculate for rows/columns */
		gw = bitmap_glyph->bitmap.width;
		gh = bitmap_glyph->bitmap.rows;
		character = malloc(sizeof(fontchar_t));
		character->x[0] = (float)((float)sx/(float)s);
		character->y[0] = (float)((float)sy/(float)s);
		character->x[1] = (float)character->x[0]+(float)((float)mw/(float)s);
		character->y[1] = (float)character->y[0]+(float)((float)mh/(float)s);
		character->a = (face->glyph->advance.x >> 6);
		character->w = mw;
		character->h = mh;

		array_push_ptr(&p->sizes,character);
		for (j=0; j<mh; j++) {
			for (k=0; k<mw; k++) {
				ax = 2*((sx+k));
				ay = 2*((sy+j)+(bl-bitmap_glyph->top));
				l = (ax+(ay*s));
				p->pixels[l] = 255;
				l++;
				if (k >= gw || j >= gh) {
					p->pixels[l] = 0;
				}else{
					p->pixels[l] = bitmap_glyph->bitmap.buffer[k+(gw*j)];
				}
			}
		}

		sx += mw;
		if (sx+mw > s) {
			sx = 0;
			sy += mh;
		}
		FT_Done_Glyph(glyph);
	}

	glGenTextures(1,&p->glid);
	glBindTexture(GL_TEXTURE_2D, p->glid);

	m = opengl_has_mipmap();

        /* draw the pixels to the texture */
	glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,s,s,0,GL_RG,GL_UNSIGNED_BYTE,p->pixels);

	if (m) {
		glGenerateMipmap(GL_TEXTURE_2D);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	}

	glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

	if (m) {
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
	}else{
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	}

	if (opengl_has_anisotropic()) {
		float ma = opengl_max_anisotropic();
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, ma);
	}

	/* done! */
	FT_Done_Face(face);
	FT_Done_FreeType(library);

	array_set_ptr(&f->pages,p,i);

	return 0;
}

/* load a true type font file */
int font_load_ttf(font_t *f, char* file)
{
	f->file = path_get("font",file,1,NULL,0);

	if (!f->file)
		return 1;

	array_init(&f->pages,ARRAY_TYPE_PTR);

	/* create the ascii fontpage */
	if (font_ttf_gen_fontpage(f,0))
		vlprintf(CN_WARN, "font_load_ttf: failed to generate ASCII fontpage");

	return 0;
}

/* returns the opengl texture id and texcoords for a character */
int font_ttf_get_char(font_t *f, uint32_t ch, fontchar_t **fc, GLuint *glid)
{
	uint32_t pid;
	fontpage_t *p;
	pid = ch/256;

	if (font_ttf_gen_fontpage(f,pid))
		return 1;

	p = array_get_ptr(&f->pages,pid);
	if (!p)
		return 1;

	*fc = array_get_ptr(&p->sizes,ch%256);
	if (!(*fc))
		return 1;

	*glid = p->glid;

	return 0;
}
